﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using Advantech.Adam;
using Advantech.Common;

namespace EONFULLSIN.Utils
{
    public class AdamHelper
    {
        /// <summary>
        /// 模块型号
        /// </summary>
        public string M_ModuleName { get; set; }

        /// <summary>
        /// 串口号
        /// </summary>
        public string M_iCom { get; set; }

        /// <summary>
        /// 广播地址
        /// </summary>
        public int M_iAddr { get; set; } = 1;

        /// <summary>
        /// 延时
        /// </summary>
        public int Timeout { get; set; } = 3000;

        ///// <summary>
        ///// 触发DO点位的队列
        ///// </summary>
        //public Queue<ConcurrentDictionary<int, bool>> M_TriggerQueue { get; set; }

        private static object _object = new object();
        public readonly Queue<ConcurrentDictionary<int, bool>> m_triggerQueue =
            new Queue<ConcurrentDictionary<int, bool>>(); //创建触发线程
        private int m_iAddr,
            m_iCount,
            m_iDITotal,
            m_iDOTotal;
        private string m_iCom,
            m_ModuleName;
        private bool m_bStart;
        private Adam4000Config m_adamConfig;
        private Adam4000Type m_Adam4000Type;
        private AdamCom adamCom;

        //回调
        public static event Action<ConcurrentDictionary<string, bool>> AnalogEvent;
        private ConcurrentDictionary<string, bool> pairs = new ConcurrentDictionary<string, bool>();
        private bool[] adamValue;

        CancellationTokenSource cansrc = new CancellationTokenSource();

        public AdamHelper() { }

        /// <summary>
        /// 实例化ADAM模块
        /// </summary>
        /// <param name="com">串口号</param>
        /// <param name="mQueue">队列</param>
        /// <param name="slaveAdr">广播地址</param>
        /// <param name="type">ADAM类型</param>
        /// <param name="timeout">超时时间</param>
        public AdamHelper(
            string com,
            Queue<ConcurrentDictionary<int, bool>> mQueue,
            int slaveAdr = 1,
            Adam4000Type type = Adam4000Type.Adam4068,
            int timeout = 3000
        )
        {
            this.m_triggerQueue = mQueue;
            m_iCom = com; // using COM1
            m_iAddr = slaveAdr; // the slave address is 1
            m_iCount = 0; // the counting start from 0
            m_bStart = false;
            m_Adam4000Type = type;

            m_iDITotal = DigitalInput.GetChannelTotal(m_Adam4000Type);
            m_iDOTotal = DigitalOutput.GetChannelTotal(m_Adam4000Type);
            if (m_Adam4000Type == Adam4000Type.Adam4068) { }
            adamCom = new AdamCom(m_iCom);
            adamCom.Checksum = false; // disabled checksum
            m_ModuleName = m_Adam4000Type.ToString();
        }

        public async void Ready()
        {
            const int retryDelayMilliseconds = 2000;
            const int maxRetries = int.MaxValue;
            int retryCount = 0;
            m_triggerQueue.Clear();
            await Task.Factory.StartNew(async () =>
            {
                while (!cansrc.IsCancellationRequested)
                {
                    try
                    {
                        if (adamCom.IsOpen)
                        {
                            if (m_triggerQueue.Count > 0)
                            {
                                var elementVal = m_triggerQueue.Dequeue();
                                foreach (var item in elementVal)
                                {
                                    TriggerDIO(item.Key, item.Value);
                                }
                            }
                            await RefresDIO();
                        }
                        else
                        {
                            ConnectPort();
                        }
                        Thread.Sleep(50);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(
                            $"Error reading ADAM data at {DateTime.Now}: {ex.Message}"
                        );
                        retryCount++;
                        if (retryCount >= maxRetries)
                        {
                            Console.WriteLine(
                                $"Failed to read ADAM data after {maxRetries} attempts. Exiting loop"
                            );
                            break;
                        }
                        await Task.Delay(retryDelayMilliseconds);
                    }
                }
            });
        }

        /// <summary>
        /// 连接
        /// </summary>
        /// <returns></returns>
        public bool ConnectPort()
        {
            try
            {
                if (adamCom.OpenComPort())
                {
                    // set COM port state, 9600,N,8,1
                    adamCom.SetComPortState(
                        Baudrate.Baud_9600,
                        Databits.Eight,
                        Advantech.Common.Parity.None,
                        Stopbits.One
                    );
                    // set COM port timeout
                    adamCom.SetComPortTimeout(200, 1000, 0, 1000, 0);
                    m_iCount = 0; // reset the reading counter
                    // get module config
                    if (!adamCom.Configuration(m_iAddr).GetModuleConfig(out m_adamConfig))
                    {
                        adamCom.CloseComPort();
                        //MessageBox.Show("Failed to get module config!", "Error");
                        return false;
                    }
                    m_bStart = true; // starting flag
                    return true;
                }
                else
                {
                    ReconnectSerialPort();
                    //MessageBox.Show("Failed to open COM port!", "Error");
                    return false;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        private void ReconnectSerialPort()
        {
            Thread.Sleep(5000); // 等待5秒后重试
            Trace.WriteLine(m_iCom + "retry..");
            ConnectPort();
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public bool ClosePort()
        {
            try
            {
                bool res = adamCom.CloseComPort();
                return res;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        public bool[] getData()
        {
            return adamValue;
        }

        /// <summary>
        /// 刷新DIO状态
        /// </summary>
        private async Task RefresDIO()
        {
            try
            {
                pairs?.Clear();
                bool[] bDI,
                    bDO;

                if (
                    adamCom
                        .DigitalInput(m_iAddr)
                        .GetValues(m_iDITotal, m_iDOTotal, out bDI, out bDO)
                )
                {
                    adamValue = bDO;
                    //if (m_iDITotal > 0)
                    //{
                    //    if (m_iDOTotal > 0) // DI > 0, DO > 0
                    //    {
                    //        if (m_iDITotal > 0)
                    //            txtCh0.Text = bDI[0];
                    //        if (m_iDITotal > 1)
                    //            txtCh1.Text = bDI[1];
                    //        if (m_iDITotal > 2)
                    //            txtCh2.Text = bDI[2];
                    //        if (m_iDITotal > 3)
                    //            txtCh3.Text = bDI[3];
                    //        if (m_iDITotal > 4)
                    //            txtCh4.Text = bDI[4];
                    //        if (m_iDITotal > 5)
                    //            txtCh5.Text = bDI[5];
                    //        if (m_iDITotal > 6)
                    //            txtCh6.Text = bDI[6];
                    //        if (m_iDITotal > 7)
                    //            txtCh7.Text = bDI[7];
                    //    }
                    //    else // DI > 0, DO = 0
                    //    {
                    //        if (m_iDITotal > 0)
                    //            txtCh0.Text = bDI[0];
                    //        if (m_iDITotal > 1)
                    //            txtCh1.Text = bDI[1];
                    //        if (m_iDITotal > 2)
                    //            txtCh2.Text = bDI[2];
                    //        if (m_iDITotal > 3)
                    //            txtCh3.Text = bDI[3];
                    //        if (m_iDITotal > 4)
                    //            txtCh4.Text = bDI[4];
                    //        if (m_iDITotal > 5)
                    //            txtCh5.Text = bDI[5];
                    //        if (m_iDITotal > 6)
                    //            txtCh6.Text = bDI[6];
                    //        if (m_iDITotal > 7)
                    //            txtCh7.Text = bDI[7];
                    //    }
                    //}
                    //else // DI = 0, DO > 0
                    //{
                    if (m_iDOTotal > 0)
                    {
                        //txtCh0.Text = bDO[0];
                        pairs?.TryAdd($"{m_iCom}:0", bDO[0]);
                    }
                    if (m_iDOTotal > 1)
                    {
                        //txtCh1.Text = bDO[1];
                        pairs?.TryAdd($"{m_iCom}:1", bDO[1]);
                    }
                    if (m_iDOTotal > 2)
                    {
                        //txtCh2.Text = bDO[2];
                        pairs?.TryAdd($"{m_iCom}:2", bDO[2]);
                    }
                    if (m_iDOTotal > 3)
                    {
                        //txtCh2.Text = bDO[2];
                        pairs?.TryAdd($"{m_iCom}:3", bDO[3]);
                    }
                    if (m_iDOTotal > 4)
                    {
                        //txtCh2.Text = bDO[2];
                        pairs?.TryAdd($"{m_iCom}:4", bDO[4]);
                    }
                    if (m_iDOTotal > 5)
                    {
                        //txtCh2.Text = bDO[2];
                        pairs?.TryAdd($"{m_iCom}:5", bDO[5]);
                    }
                    if (m_iDOTotal > 6)
                    {
                        //txtCh2.Text = bDO[2];
                        pairs?.TryAdd($"{m_iCom}:6", bDO[6]);
                    }
                    if (m_iDOTotal > 7)
                    {
                        //txtCh2.Text = bDO[2];
                        pairs?.TryAdd($"{m_iCom}:7", bDO[7]);
                    }
                    AnalogEvent?.Invoke(pairs);
                    //}
                }
                else
                {
                    pairs.Clear();
                    for (int i = 0; i < 8; i++)
                    {
                        pairs?.TryAdd($"{m_iCom}:" + (i + 1).ToString(), false);
                    }
                    AnalogEvent?.Invoke(pairs);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 触发
        /// </summary>
        /// <param name="i_iCh"></param>
        /// <returns></returns>
        public bool TriggerDIO(int i_iCh, bool value)
        {
            bool bRet;
            if (m_iDITotal > 0)
            {
                i_iCh = i_iCh - 8;
            }
            if (m_iDOTotal > 8)
                bRet = adamCom.DigitalOutput(m_iAddr).SetSValue(i_iCh, value);
            else
                bRet = adamCom.DigitalOutput(m_iAddr).SetValue(i_iCh, value);
            //if (!bRet)
            //    return false;
            return bRet;
        }
    }

    /// <summary>
    /// IO模块A
    /// </summary>
    public enum DoPoint_A
    {
        K01 = 0,
        K02 = 1,
        K03 = 2,
        K04 = 3,
        K05 = 4,
        K06 = 5,
        K07 = 6,
        K08 = 7,
    }

    /// <summary>
    /// IO模块B
    /// </summary>
    public enum DoPoint_B
    {
        K09 = 0,
        K10 = 1,
        K11 = 2,
        K12 = 3,
        K13 = 4,
        K14 = 5,
        K15 = 6,
        K16 = 7,
    }
}
